package mtt.control;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import mtt.model.calendar.Week;

public class CalendarController {

	/**
	 * enthält die einzige CalendarController Instanz
	 */
	private static final CalendarController calendarController = new CalendarController();

	/**
	 * enthält die anzahl der Millisekunden eines Tages
	 */
	public static final long millisOfADay = 86400000; /* 1000 * 60 * 60 * 24 */

	/**
	 * der Wert von typOfCurrentWeek entspricht einer geraden Woche
	 */
	public static final boolean typOfCurrentWeekGerade = false;

	/**
	 * der Wert von typOfCurrentWeek entspricht einer ungeraden Woche
	 */
	public static final boolean typOfCurrentWeekUngerade = true;

	/**
	 * Factory-Methode, liefert immer das gleiche CalendarController-Objekt
	 * 
	 * @return CalendarController
	 */
	public static final CalendarController getInstance() {
		return CalendarController.calendarController;
	}

	/**
	 * enthält den aktuellen Calendar für die default Timezone
	 */
	private Calendar calendarCurrent = Calendar.getInstance(TimeZone
			.getDefault());

	/**
	 * enthält das aktuelle Datum
	 */
	private Date dateCurrent = new Date();

	/**
	 * enthält den Typ (gerade/ungerade) der aktuellen Woche
	 */
	private boolean typOfCurrentWeek;

	/**
	 * enthält das Week-Objekt der aktuellen Woche
	 */
	private Week weekCurrent = new Week();

	/**
	 * initialisiert den internen Calendar mit dem aktuellen Date
	 */
	private CalendarController() {
		calendarCurrent.setTime(dateCurrent);
		initCurrrentWeek();
	}

	/**
	 * liefert ein Week-Objekt der aktuellen Woche
	 * 
	 * @return current Week
	 */
	public Week getCurrentWeek() {
		return this.weekCurrent;
	}

	/**
	 * liefert das Datum der Woche in der Form: 27.08 - 02.09
	 * 
	 * dateFirstDayOfWeek und dateLastDayOfWeek muss gesetzt sein
	 * 
	 * @param Week
	 *            für die der String generiert werden soll
	 */
	public String getStringOfWeek(Week week) {
		/*
		 * Calendar mit dem Zeitpunkt des ersten Tags der Woche erstellen
		 */
		Calendar calendarFirstDayOfWeek = Calendar.getInstance(TimeZone
				.getDefault());
		calendarFirstDayOfWeek.setTime(week.getDateFirstDayOfWeek());

		/*
		 * DAY_OF_MONTH des ersten Tags der Woche ermitteln
		 */
		int dayOfMonthFirstDayOfWeek = calendarFirstDayOfWeek
				.get(Calendar.DAY_OF_MONTH);

		/*
		 * Calendar mit dem Zeitpunkt des letzten Tags der Woche erstellen
		 */
		Calendar calendarLastDayOfWeek = Calendar.getInstance(TimeZone
				.getDefault());
		calendarLastDayOfWeek.setTime(week.getDateLastDayOfWeek());

		/*
		 * DAY_OF_MONTH des letzten Tags der Woche ermitteln
		 */
		int dayOfMonthLastDayOfWeek = calendarLastDayOfWeek
				.get(Calendar.DAY_OF_MONTH);

		/*
		 * String-Abbild des Datums erstellen
		 */
		String sFirstDayOfWeek;
		if (dayOfMonthFirstDayOfWeek < 10) {
			sFirstDayOfWeek = "0" + dayOfMonthFirstDayOfWeek;
		} else {
			sFirstDayOfWeek = "" + dayOfMonthFirstDayOfWeek;
		}

		String sLastDayOfWeek;
		if (dayOfMonthLastDayOfWeek < 10) {
			sLastDayOfWeek = "0" + dayOfMonthLastDayOfWeek;
		} else {
			sLastDayOfWeek = "" + dayOfMonthLastDayOfWeek;
		}

		int monthOfFirstDayOfWeek = (calendarFirstDayOfWeek.get(Calendar.MONTH) + 1);
		String sMonthOfFirstDayOfWeek;
		if (monthOfFirstDayOfWeek < 10) {
			sMonthOfFirstDayOfWeek = "0" + monthOfFirstDayOfWeek;
		} else {
			sMonthOfFirstDayOfWeek = "" + monthOfFirstDayOfWeek;
		}

		int monthOfLastDayOfWeek = (calendarLastDayOfWeek.get(Calendar.MONTH) + 1);
		String sMonthOfLastDayOfWeek;
		if (monthOfLastDayOfWeek < 10) {
			sMonthOfLastDayOfWeek = "0" + monthOfLastDayOfWeek;
		} else {
			sMonthOfLastDayOfWeek = "" + monthOfLastDayOfWeek;
		}

		/*
		 * String mit dem Datum der Woche erstellen
		 */
		String dateOfWeek = sFirstDayOfWeek + "." + sMonthOfFirstDayOfWeek
				+ " - " + sLastDayOfWeek + "." + sMonthOfLastDayOfWeek;
		return dateOfWeek;
	}

	/**
	 * liefert den Typ der aktuellen Woche
	 * 
	 * @return true = ungerade Woche, false = gerade Woche
	 */
	public boolean getTypOfCurrentWeek() {
		return this.typOfCurrentWeek;
	}

	/**
	 * initialisiert die aktuelle Woche
	 */
	private void initCurrrentWeek() {
		/*
		 * Date des aktuellen Zeipunkts erstellen
		 */
		Date date = new Date();

		setWeekNumber(date, this.weekCurrent);
	}

	/**
	 * setzt das Date des ersten und des letzten Tags der Woche
	 * 
	 * @param Date
	 *            für welches der erste und letzte Tag der Woche gesetzt werden
	 *            soll
	 * @param Week
	 *            in die die Date Objekt eingetragen werden soll
	 */
	private void setDateOfFirstAndLastDayOfWeek(Date date, Week week) {

		Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
		calendar.setTime(date);

		/*
		 * zum ersten Tag der Woche zurück rechnen
		 */

		/*
		 * DAY_OF_WEEK des aktuellen Tags
		 */
		int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);

		/*
		 * wenn der aktuelle Tag ein Sonntag ist
		 */
		if (dayOfWeek == calendar.get(Calendar.SUNDAY)) {
			dayOfWeek = 8;
		}

		/*
		 * ersten Tag der Woche setzen
		 */
		int firstDayOfWeek = Calendar.MONDAY;

		/*
		 * wieviele Tage liegen zwischen dem ersten und dem aktuellen Tag der
		 * Woche
		 */
		int diffDays = dayOfWeek - firstDayOfWeek;

		/*
		 * zum Zeitpunkt des ersten Tags der Woche zurück rechnen
		 */
		long millisOfFirstDayOfWeek = date.getTime()
				- (diffDays * millisOfADay);
		Date dateFirstDayOfWeek = new Date(millisOfFirstDayOfWeek);

		/*
		 * ersten Tag der Woche im Week-Objekt setzen
		 */
		week.setDateFirstDayOfWeek(dateFirstDayOfWeek);

		/*
		 * letzten Tag der Woche ermitteln
		 */
		long millisOfLastDayOfWeek = millisOfFirstDayOfWeek
				+ (6 * millisOfADay);
		Date dateLastDayOfWeek = new Date(millisOfLastDayOfWeek);

		/*
		 * letzten Tag der Woche im Week-Objekt setzen
		 */
		week.setDateLastDayOfWeek(dateLastDayOfWeek);
	}

	/**
	 * setzt die Nummer der Woche (1-52)
	 * 
	 * @param Date-Objekt
	 *            für welches die weekNumber errechnet werden soll
	 * @param Week-Objekt
	 *            welches die die weekNumber erhalten soll
	 */
	private void setWeekNumber(Date date, Week week) {
		/*
		 * ersten und letzten Tag der Woche setzen
		 */
		setDateOfFirstAndLastDayOfWeek(date, week);

		/*
		 * Calendar des Date-Objekts erzeugen
		 */
		Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
		calendar.setTime(date);

		/*
		 * Kalender des 01.01. des aktuellen Jahres erstellen
		 */
		Calendar calendarFirstDayOfYear = Calendar.getInstance(TimeZone
				.getDefault());

		calendarFirstDayOfYear.set(Calendar.DAY_OF_MONTH, 1);
		calendarFirstDayOfYear.set(Calendar.MONTH, Calendar.JANUARY);

		calendarFirstDayOfYear.set(Calendar.HOUR_OF_DAY, 0);
		calendarFirstDayOfYear.set(Calendar.MINUTE, 0);
		calendarFirstDayOfYear.set(Calendar.SECOND, 0);
		calendarFirstDayOfYear.set(Calendar.MILLISECOND, 0);

		/*
		 * Jahr des Date-Objekts eintragen
		 */
		calendarFirstDayOfYear.set(Calendar.YEAR, calendar.get(Calendar.YEAR));

		/*
		 * Datum des 01.01. des aktuellen Jahres ermitteln
		 */
		Date dateFirstDayOfYear = calendarFirstDayOfYear.getTime();

		/*
		 * Der erste Tag der Woche ist Montag, der erste Tag des Jahres meistens
		 * nicht, die erste Woche des Jahres fängt am Montag in der Woche mit
		 * dem ersten Tag des Jahres an, also muss vom 01.01 des Jahres noch zum
		 * ersten Tag der Woche zurück gerechnet werden, da sonst jede Woche an
		 * dem Tag anfängt an dem der erste Tag des Jahres war
		 */

		/*
		 * Time des ersten Tags des Jahres - der Zeit die seit dem Tag in der
		 * Woche vergangen ist (zum ersten Tag der Woche springen)
		 * 
		 * -2 weil im j2me Calender die Woche am Sonntag beginnte, nicht am
		 * Montag
		 */
		long longTimeOfFirstDayOfFirstWeek = dateFirstDayOfYear.getTime()
				- ((calendarFirstDayOfYear.get(Calendar.DAY_OF_WEEK) - 2) * millisOfADay);

		/*
		 * anzahl der Wochen die seit dem ersten Tag der ersten Woche vergangen
		 * sind, ermitteln
		 */
		long weekCount = (dateCurrent.getTime() - longTimeOfFirstDayOfFirstWeek)
				/ (1000 * 60 * 60 * 24 * 7);

		// TODO: prüfen ob das long wirklich in ein int gecastet werden kann
		week.setIntWeekNumber((int) weekCount +1);

	}
}
